﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using Phone.Core.Helpers;
using System.Collections.Generic;
using Toggl.PhoneClient.Domain;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Text;
using System.Linq;
using Newtonsoft.Json.Converters;

namespace Toggl.PhoneClient.Services
{
    public class TogglRemoteService : IRemoteService
    {
        private const string AuthUrl = "http://www.toggl.com/api/v3/me.json";
        private const string TasksUrl = "http://www.toggl.com/api/v3/tasks.json";
        private const string WorkspacesUrl = "http://www.toggl.com/api/v3/workspaces.json";
        private const string ClientsUrl = "http://www.toggl.com/api/v3/clients.json";
        private const string ProjectsUrl = "http://www.toggl.com/api/v3/projects.json";
        private const string PlannedTasksUrl = "http://www.toggl.com/api/v3/planned_tasks.json";
        private const string TagsUrl = "http://www.toggl.com/api/v3/tags.json";
        private const string ProjectsUpdateUrl = "http://www.toggl.com/api/v3/projects/{0}.json";
        private KeyValuePair<string, string> _Authorization;

        public KeyValuePair<string, string> Authorization
        {
            get { return _Authorization; }
            internal set { _Authorization = value; }
        }


        internal void CreateAuthFromToken(string token)
        {
            string authValue = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:{1}", token, "api_token")));
            this.Authorization = new KeyValuePair<string, string>("Authorization", authValue);
        }

        private void ProcessResponse<T>(HttpResponseCompleteEventArgs args, Action<T, Exception> callback) where T : class
        {
            if (args.Error != null)
            {
                callback(null, args.Error);
                return;
            }
            JToken jsonResult = JObject.Parse(args.Response)["data"];
            T result = JsonConvert.DeserializeObject<T>(jsonResult.ToString());
            callback(result, null);
        }
        private void ProcessRequest<T>(Uri uri, Action<T, Exception> callback) where T : class
        {
            if (string.IsNullOrEmpty(this.Authorization.Key))
            {
                throw new InvalidOperationException(Resources.AppResources.AuthNull);
            }
            HttpHelper helper = new HttpHelper(uri);
            HttpResponseCompleteEventHandler response = e =>
            {
                this.ProcessResponse<T>(e, callback);
            };
            helper.ResponseComplete += response;
            helper.ExecuteGet(this.Authorization);
        }

        #region IRemoteService Members
        public void Auth(string email, string password, Action<User, Exception> callback)
        {
            Uri authUri = new Uri(AuthUrl);
            HttpHelper helper = new HttpHelper(authUri);

            HttpResponseCompleteEventHandler response = e =>
            {
                if (e.Error != null)
                {
                    callback(null, e.Error);
                    return;
                }
                JObject jsonResult = JObject.Parse(e.Response);
                JToken result = jsonResult["data"];
                User user = JsonConvert.DeserializeObject<User>(result.ToString());
                this.CreateAuthFromToken(user.apiToken);
                callback(user, null);
            };

            helper.ResponseComplete += response;
            string credential = string.Format("{0}:{1}", email, password);
            credential = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(credential));
            helper.ExecuteGet(new KeyValuePair<string, string>("Authorization", credential));
        }

        public void GetTasks(Action<List<Task>, Exception> callback)
        {
            this.GetTasks(null, null, callback);
        }

        public void GetTasks(DateTime? from, DateTime? to, Action<List<Task>, Exception> callback)
        {
            string query = string.Empty;
            if (from.HasValue && to.HasValue)
            {
                query = string.Format("?start_date={0}&end_date={1}", from.Value.ToString("yyyy-MM-ddThh:mm:sszzz"), to.Value.ToString("yyyy-MM-ddThh:mm:sszzz"));
            }
            this.ProcessRequest<List<Task>>(new Uri(TasksUrl + query), callback);
        }

        public void GetProjects(Action<List<Project>, Exception> callback)
        {
            this.ProcessRequest<List<Project>>(new Uri(ProjectsUrl), callback);
        }

        public void GetWorkspaces(Action<List<Workspace>, Exception> callback)
        {
            this.ProcessRequest<List<Workspace>>(new Uri(WorkspacesUrl), callback);
        }

        public void GetClients(Action<List<Client>, Exception> callback)
        {
            this.ProcessRequest<List<Client>>(new Uri(ClientsUrl), callback);
        }

        public void GetTags(Action<List<Tag>, Exception> callback)
        {
            this.ProcessRequest<List<Tag>>(new Uri(TagsUrl), callback);
        }

        public void GetPlannedTasks(Action<List<PlannedTask>, Exception> callback)
        {
            this.ProcessRequest<List<PlannedTask>>(new Uri(PlannedTasksUrl), callback);
        }
        public void CreateTask(Task task, Action<Task, Exception> callback)
        {
            throw new NotImplementedException();

        }

        public void UpdateTask(Task task, Action<Task, Exception> callback)
        {
            throw new NotImplementedException();
        }

        public void DeleteTask(int taskId, Action<Task, Exception> callback)
        {
            throw new NotImplementedException();
        }

        public void CreateProject(Project project, Action<Project, Exception> callback)
        {
            if (project == null)
                throw new ArgumentNullException("project");

            Uri projectsUri = new Uri(ProjectsUrl);
            HttpHelper helper = new HttpHelper(projectsUri);

            HttpResponseCompleteEventHandler response = e =>
            {
                this.ProcessResponse<Project>(e, callback);
            };

            helper.ResponseComplete += response;
            CreateProject newProject = new Services.CreateProject(project);
            string value = JsonConvert.SerializeObject(newProject, new IsoDateTimeConverter());
            helper.ExecutePostJson(value, this.Authorization);
        }

        public void UpdateProject(Project project, Action<Project, Exception> callback)
        {
            if (project == null)
                throw new ArgumentNullException("project");

            Uri projectsUri = new Uri(string.Format(ProjectsUpdateUrl, project.Id.Value));
            HttpHelper helper = new HttpHelper(projectsUri);

            HttpResponseCompleteEventHandler response = e =>
            {
                this.ProcessResponse<Project>(e, callback);
            };

            CreateProject updatedProject = new Services.CreateProject(project);
            string value = JsonConvert.SerializeObject(updatedProject, new IsoDateTimeConverter());
            helper.ResponseComplete += response;
            helper.ExecutePutJson(value, this.Authorization);
        }

        #endregion
    }

    [JsonObject]
    public class CreateTask
    {
        public CreateTask(Task task)
        {
            this.Task = task;
        }

        [JsonProperty(PropertyName = "task")]
        public Task Task { get; set; }
    }

    [JsonObject]
    public class CreateProject
    {
        [JsonProperty(PropertyName = "project")]
        public Project Project { get; set; }
        public CreateProject(Project project)
        {
            this.Project = project;
        }
    }
}
